﻿//-------------------------------------------------------------------
// CVORegistry implementation file
//-------------------------------------------------------------------
//
// Copyright ?000-2003 Virtual Office Systems Incorporated
// All Rights Reserved
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed unmodified by any means PROVIDING it is
// not sold for profit without the authors written consent, and
// providing that this notice and the authors name is included.
//
// This code can be compiled, modified and distributed freely, providing
// that this copyright information remains intact in the distribution.
//
// This code may be compiled in original or modified form in any private
// or commercial application.
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability for any damage, in any form, caused
// by this code. Use it at your own risk.
//-------------------------------------------------------------------

#include "stdafx.h"
#include "VORegistry.h"
#include "CETool.h"


// Verify that a pointer points to valid memory
inline BOOL IsValidAddress(const void* p,
                           size_t /*nBytes*/,
                           BOOL /*bReadWrite = TRUE*/)
{
	return (p != NULL);
}

// Verify that a null-terminated string points to valid memory
inline BOOL IsValidString(LPCTSTR psz,
                          size_t nMaxLength = UINT_MAX)
{
	if ( 0 == nMaxLength )
	{
		return FALSE;
	}

	return (psz != NULL);
}


BOOL DeleteSubkey(HKEY hkey, LPCTSTR pcszSubkey);

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CVORegistry::CVORegistry(const CVORegistry& rSrc)
{
	if ( rSrc.m_hkey )
	{
		if ( ::RegCreateKeyEx(rSrc.m_hkey, rSrc.m_strSubkey,
		                      0, NULL, 0, KEY_ALL_ACCESS, NULL,
		                      &m_hkey, &m_dwDisposition) != ERROR_SUCCESS )
		{
			::OutputDebugString(TEXT("CVORegistry() Copy Constructor: Unable to create subkey: "));
			::OutputDebugString(rSrc.m_strSubkey);
			::OutputDebugString(TEXT("\n"));
		}
	}
	else
	{
		m_hkey = 0;
	}

	m_strSubkey = rSrc.m_strSubkey;
	m_nSubkeyIndex = 0;
	m_dwDisposition = rSrc.m_dwDisposition;
}

CVORegistry::CVORegistry(HKEY hkeyParent, LPCTSTR pcszSubkey, BOOL fCreateIfNew)
		: m_dwDisposition(0),
		m_nSubkeyIndex(0)
{
	if ( fCreateIfNew )
	{
		if ( ::RegCreateKeyEx(hkeyParent, pcszSubkey,
		                      0, NULL, 0, KEY_ALL_ACCESS, NULL,
		                      &m_hkey, &m_dwDisposition) != ERROR_SUCCESS )
		{
			MTRACE(TEXT("CVORegistry() Constructor: Unable to create subkey: %s\n"), pcszSubkey);
		}
	}
	else
	{
		if ( ::RegOpenKeyEx(hkeyParent, pcszSubkey, 0, KEY_ALL_ACCESS, &m_hkey) != ERROR_SUCCESS )
		{
			m_hkey = 0;
			MTRACE(TEXT("CVORegistry() Constructor: Unable to open subkey: %s\n"), pcszSubkey);
		}
	}
}

CVORegistry::~CVORegistry()
{
	::RegCloseKey(m_hkey);
}

BOOL CVORegistry::Close()
{
	if ( m_hkey )
	{
		::RegCloseKey(m_hkey);
		m_hkey = 0;

		return TRUE;
	}

	return FALSE;
}

BOOL CVORegistry::ReadBinary(LPCTSTR pcszKey, LPBYTE pData, DWORD dwBufferSize)
{
	return ReadBinary(pcszKey, pData, &dwBufferSize);	// Note - buffer size not passed to caller
}

BOOL CVORegistry::ReadBinary(LPCTSTR pcszKey, LPBYTE pData, LPDWORD pcbData)
{
	if ( pData && pcbData && *pcbData && (!IsValidAddress(pData, *pcbData, TRUE)) )
	{
		MTRACE(L"CVORegistry::ReadBinary() Invalid buffer specified\n");

		return FALSE;
	}

	DWORD dwType = REG_BINARY;

	return (::RegQueryValueEx(m_hkey, pcszKey, 0, &dwType, (PBYTE)pData, pcbData) == ERROR_SUCCESS);
}

BOOL CVORegistry::WriteBinary(LPCTSTR pcszKey, LPBYTE pData, DWORD cbData)
{
	if ( !IsValidAddress(pData, cbData, FALSE) )
	{
		MTRACE(TEXT("CVORegistry::WriteBinary() Invalid buffer specified\n"));

		return FALSE;
	}

	return (::RegSetValueEx(m_hkey, pcszKey, 0, REG_BINARY, (PBYTE)pData, cbData) == ERROR_SUCCESS);
}

DWORD CVORegistry::ReadDWORD(LPCTSTR pcszKey, DWORD dwDefault)
{
	DWORD dwValue;
	DWORD dwValueSize = sizeof(DWORD);
	DWORD dwType = REG_DWORD;

	if ( ::RegQueryValueEx(m_hkey, pcszKey, 0, &dwType, (PBYTE)&dwValue, &dwValueSize) == ERROR_SUCCESS )
	{
		return dwValue;
	}

	if ( dwDefault != DEFAULT_DWORD )	// Default specified
	{
		::RegSetValueEx(m_hkey, pcszKey, 0, REG_DWORD, (PBYTE)&dwDefault, sizeof(DWORD));
	}

	return dwDefault;
}

BOOL CVORegistry::WriteDWORD(LPCTSTR pcszKey, DWORD dwValue)
{
	return (::RegSetValueEx(m_hkey, pcszKey,
	                        0, REG_DWORD,
	                        (PBYTE)&dwValue, sizeof(DWORD)) == ERROR_SUCCESS);
}

CVOString CVORegistry::ReadString(LPCTSTR pcszKey, LPCTSTR pcszDefault)
{
	DWORD	dwDataSize = 0;
	DWORD	dwType = REG_SZ;

	if ( ::RegQueryValueEx(m_hkey, pcszKey, 0, &dwType, (PBYTE)NULL, &dwDataSize) == ERROR_SUCCESS )
	{
		CVOString strValue;

		if ( dwType != REG_SZ )
		{
			if ( pcszDefault )
			{
				strValue = pcszDefault;
			}

			return strValue;
		}

		if ( ::RegQueryValueEx(m_hkey, pcszKey, 0, &dwType,
		                       (PBYTE)(LPTSTR)strValue.GetBuffer(dwDataSize + 1),
		                       &dwDataSize) == ERROR_SUCCESS )
		{
			strValue.ReleaseBuffer();

			return strValue;
		}
		else
		{
			strValue.ReleaseBuffer();
			MTRACE(L"CVORegistry(%s)::ReadString() Unable to read variable %s\n", (LPCTSTR)m_strSubkey, pcszKey);
		}
	}

	// Write the default value (if any)  Note: If there was a value in the registry, it
	// would already have been written
	if ( IsValidString(pcszDefault) )
	{
		if ( !WriteString(pcszKey, pcszDefault) )
		{
			MTRACE(L"CVORegistry(%s)::ReadString() Unable to write value %s to variable %s\n",
			       (LPCTSTR)m_strSubkey, pcszDefault, pcszKey);
		}

		return pcszDefault;
	}
	else
	{
		return L"";
	}
}

BOOL CVORegistry::WriteString(LPCTSTR pcszKey, LPCTSTR pcszValue)
{
	if ( !IsValidString(pcszValue) )
	{
		MTRACE(L"CVORegistry::WriteString() Invalid Value specified\n");

		return FALSE;
	}

	return (::RegSetValueEx(m_hkey, pcszKey,
	                        0, REG_SZ, (PBYTE)pcszValue,
	                        (_tcslen(pcszValue) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS);
}

BOOL CVORegistry::DeleteSubkey(LPCTSTR pcszSubkey)
{
	return ::DeleteSubkey(m_hkey, pcszSubkey);
}

BOOL CVORegistry::GetFirstSubkey(CVOString &strSubkey)
{
	DWORD dwNameSize = MAX_PATH;

	m_nSubkeyIndex = 0;

	if ( ::RegEnumKeyEx(m_hkey, (DWORD)m_nSubkeyIndex,
	                    strSubkey.GetBuffer(dwNameSize),
	                    &dwNameSize, 0, NULL, NULL, NULL) != ERROR_SUCCESS )
	{
		strSubkey.Empty();

		return FALSE;	// No sub keys
	}

	strSubkey.ReleaseBuffer();

	return TRUE;
}

BOOL CVORegistry::GetNextSubkey(CVOString &strSubkey)
{
	DWORD dwNameSize = MAX_PATH;

	++m_nSubkeyIndex;

	if ( ::RegEnumKeyEx(m_hkey, (DWORD)m_nSubkeyIndex,
	                    strSubkey.GetBuffer(dwNameSize), &dwNameSize,
	                    0, NULL, NULL, NULL) != ERROR_SUCCESS )
	{
		strSubkey.Empty();

		return FALSE;	// No more subkeys
	}

	strSubkey.ReleaseBuffer();

	return TRUE;
}

BOOL CVORegistry::GetSubkey(CVOString &strSubkey, int nOffset)
{
	DWORD	dwNameSize = MAX_PATH;
	LONG	rc;

	m_nSubkeyIndex = nOffset;

	rc = RegEnumKeyEx(m_hkey, (DWORD)m_nSubkeyIndex,
					  strSubkey.GetBuffer(dwNameSize), &dwNameSize, 0, NULL, NULL, NULL);
	strSubkey.ReleaseBuffer();

	if ( rc != ERROR_SUCCESS )
	{
		if ( rc != ERROR_NO_MORE_ITEMS )
		{
			MTRACE(L"CVORegistry::GetSubkey() : RegEnumKeyEx() Error#%ld\n", GetLastError());
		}

		strSubkey.Empty();

		return FALSE;	// No subkeys
	}

	return TRUE;
}


BOOL CVORegistry::SubkeyExists(LPCTSTR pcszSubkey)
{
	HKEY hkeySubkey;
	DWORD dwRC = (DWORD)::RegOpenKeyEx(m_hkey, pcszSubkey, 0, KEY_ALL_ACCESS, &hkeySubkey);

	if ( dwRC != ERROR_SUCCESS )
	{
		return FALSE;
	}

	::RegCloseKey(hkeySubkey);

	return TRUE;
}

BOOL CVORegistry::DeleteValue(LPCTSTR pcszValueName)
{
	return (::RegDeleteValue(m_hkey, pcszValueName) == ERROR_SUCCESS);
}

DWORD CVORegistry::GetDisposition() const
{
	return m_dwDisposition;
}

BOOL CVORegistry::GetFirstValue(CVOString &strValueName)
{
	DWORD dwNameSize = MAX_PATH;

	m_nSubkeyIndex = 0;

	if ( ::RegEnumValue(m_hkey, (DWORD)m_nSubkeyIndex,
	                    strValueName.GetBuffer(dwNameSize),
	                    &dwNameSize, 0, NULL, NULL, NULL) != ERROR_SUCCESS )
	{
		strValueName.Empty();

		return FALSE;	// No sub keys
	}

	strValueName.ReleaseBuffer();

	return TRUE;
}

BOOL CVORegistry::GetNextValue(CVOString &strValueName)
{
	DWORD dwNameSize = MAX_PATH;

	++m_nSubkeyIndex;

	if ( ::RegEnumValue(m_hkey, (DWORD)m_nSubkeyIndex,
	                    strValueName.GetBuffer(dwNameSize),
	                    &dwNameSize, 0, NULL, NULL, NULL) != ERROR_SUCCESS )
	{
		strValueName.Empty();

		return FALSE;	// No sub keys
	}

	strValueName.ReleaseBuffer();

	return TRUE;
}

BOOL CVORegistry::ValueExists(LPCTSTR pcszValueName)
{
	if ( !IsValidString(pcszValueName) )
	{
		MTRACE(L"CVORegistry::ValueExists() Invalid Key specified\n");

		return FALSE;
	}

	DWORD	dwDataSize = 0;
	DWORD	dwType = REG_SZ;

	return (::RegQueryValueEx(m_hkey, pcszValueName, 0, &dwType, (PBYTE)NULL, &dwDataSize) == ERROR_SUCCESS);
}

// Recursively delete a key in the registry
BOOL DeleteSubkey(HKEY hkey, LPCTSTR pcszSubkey)
{
	if ( !pcszSubkey )
	{
		return FALSE;
	}
	else
	{
		// Recursively delete any sub keys for the target sub key
		HKEY	hkeySubkey;
		CVOString	strSubkey;

		DWORD dwRC = (DWORD)::RegOpenKeyEx(hkey, pcszSubkey, 0, KEY_ALL_ACCESS, &hkeySubkey);

		if (dwRC != ERROR_SUCCESS)
		{
			return FALSE;
		}

		DWORD dwNameSize = MAX_PATH;

		while ( ::RegEnumKeyEx(hkeySubkey, 0,
		                       strSubkey.GetBuffer(dwNameSize),
		                       &dwNameSize, 0, NULL, NULL, NULL) == ERROR_SUCCESS )
		{
			strSubkey.ReleaseBuffer();

			if ( !DeleteSubkey(hkeySubkey, strSubkey) )
			{
				::RegCloseKey(hkeySubkey);

				return FALSE;
			}

			dwNameSize = MAX_PATH;
		}

		strSubkey.ReleaseBuffer();

		::RegCloseKey(hkeySubkey);
	}

	return (::RegDeleteKey(hkey, pcszSubkey) == ERROR_SUCCESS);
}


enum ValueType CVORegistry::GetValueType(LPCTSTR pcszValueName)
{
	DWORD dwType = 0;

	if ( ::RegQueryValueEx(*this, pcszValueName, 0, &dwType, NULL, NULL) != ERROR_SUCCESS )
	{
		return typeError;
	}

	switch (dwType)
	{
	case REG_BINARY:
		return typeBinary;

	case REG_DWORD:
	case REG_DWORD_BIG_ENDIAN:
		return typeDWORD;

	case REG_EXPAND_SZ:
	case REG_SZ:
		return typeString;

	case REG_MULTI_SZ:
		return typeStringList;

	default:
		return typeError;	// There are other types, but not supported by CVORegistry
	}
}

DWORD CVORegistry::GetValueSize(LPCTSTR pcszValueName)
{
	DWORD dwSize = 0;

	RegQueryValueEx(*this, pcszValueName, 0, NULL, NULL, &dwSize);

	return dwSize;
}

CVORegistry::operator HKEY()
{
	return m_hkey;
}

CVORegistry::operator HKEY*()
{
	return &m_hkey;
}

CVORegistry::operator LPCTSTR() const
{
	return m_strSubkey;
}
